import * as fs from 'fs'
import { promisify } from 'util'
import * as path from 'path'
import * as crypto from 'crypto'
import * as less from 'less';
import { Plugin } from 'esbuild'
import * as postcss from 'postcss'
import * as postcssModules from 'postcss-modules'

const md5 = (content: string) => crypto.createHash('md5').update(content).digest('hex')

export interface Options {
    root?: string
    lessOptions?: Less.Options
    postcssPlugin?: postcss.AcceptedPlugin[]
    injectScripts?: (id: string, content: string) => string
    /**
     * 是否生成 .d.ts 描述文件  
     * @description 配置true时，请添加 ignore  *.less.d.ts 或者 *.css.d.ts
     * */
    declaration?: boolean
}

const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)
export default function ({
    lessOptions = {},
    postcssPlugin = [],
    injectScripts = (id, content = '') => `(() => {
        function inject () {
            if (!document.getElementById("${id}")) {
                const _style_ = document.createElement("style");
                _style_.innerHTML = \`${content}\`;
                _style_.id = "${id}"
                document.body.appendChild(_style_)
            }
        }
        if (document.body) {
            inject()
        } else {
            document.addEventListener('DOMContentLoaded', inject)
        }
    })()`,
    declaration,
}: Options = {}) {
    const result: Plugin = {
        name: 'plugin-postcss',
        setup (build) {
            build.onLoad(
                {
                    filter: /\.less$/,
                    namespace: 'file'
                },
                async function (args) {
                    const content = await readFile(args.path);
                    const info = path.parse(args.path);
                    
                    const res = await less.render(content.toString(), {
                        javascriptEnabled: true,
                        paths: [...(lessOptions.paths || []), info.dir],
                        // compress: !!build
                        rootpath: info.dir,
                        ...lessOptions,
                    });

                    let infos: any = {}
                    const result = await postcss.default([
                        postcssModules({
                            getJSON(cssFilename, json, outputFilename?) {
                                Object.assign(infos, json)
                            },
                        }),
                        ...postcssPlugin
                    ])
                    .process(res.css, { from: args.path })

                    const id = md5(args.path + result.css)
                    if (declaration) {
                        writeFile(args.path + '.d.ts', `declare const content: Record<${
                            Object.keys(infos).map(k => `"${k}"`).join(' | ')
                        }, string>; export default content;`)
                    }
                    return {
                        contents: `${injectScripts(id, result.css)};
                        \nexport default ${JSON.stringify(infos)}`,
                        loader: 'js',
                    };
                }
            )
        }
    }
    return result
}